using System;
using System.Collections.Generic;
using System.Text;

namespace OnlinePlots.Plots.DataSets
{
    public class ContinousPlotDataSet : PlotDataSet
    {
        public delegate double FunctionDelegate(double x);
        private FunctionDelegate function = delegate(double x) { return 0; };

        public FunctionDelegate Function
        {
            get { return function; }
            set { function = value; }
        }

        private int stepsCount = 1000;
        private Interval lastUsedArgumentInterval = new Interval(0, 0);
        private double lastFoundMinValue = double.NegativeInfinity;
        private double lastFoundMinValueArgument = double.NaN;
        private double lastFoundMaxValue = double.PositiveInfinity;
        private double lastFoundMaxValueArgument = double.NaN;
        private double lastFoundMinFiniteValue = double.MinValue;
        private double lastFoundMinFiniteValueArgument = double.NaN;
        private double lastFoundMaxFiniteValue = double.MaxValue;
        private double lastFoundMaxFiniteValueArgument = double.NaN;

        private void FindExtremaValues(double minArgument, double maxArgument)
        {
            Interval theInterval = new Interval(minArgument, maxArgument);
            Interval intersectedInterval = lastUsedArgumentInterval.IntersectWith(theInterval);
            if (!intersectedInterval.IsEmpty() && intersectedInterval.ContainsValue(lastFoundMinValueArgument) && intersectedInterval.ContainsValue(lastFoundMaxValueArgument) && intersectedInterval.ContainsValue(lastFoundMinFiniteValueArgument) && intersectedInterval.ContainsValue(lastFoundMaxFiniteValueArgument))
            {
                Interval[] newIntervals = lastUsedArgumentInterval.Minus(intersectedInterval);
                foreach (Interval newInterval in newIntervals)
                {
                    int newIntervalStepsCount = (int)Math.Max(stepsCount * 0.01 + 1, stepsCount * newInterval.Length / theInterval.Length);
                    double step = newInterval.Length / newIntervalStepsCount;
                    for (double argumentValue = newInterval.MinValue; argumentValue <= newInterval.MaxValue; argumentValue += step)
                    {
                        double functionValue = GetValue(argumentValue);
                        if (!double.IsNaN(functionValue))
                        {
                            if (functionValue < lastFoundMinValue)
                            {
                                lastFoundMinValue = functionValue;
                                lastFoundMinValueArgument = argumentValue;
                            }
                            if (functionValue > lastFoundMaxValue)
                            {
                                lastFoundMaxValue = functionValue;
                                lastFoundMaxValueArgument = argumentValue;
                            }
                            if (!double.IsInfinity(functionValue) && functionValue < lastFoundMinFiniteValue)
                            {
                                lastFoundMinFiniteValue = functionValue;
                                lastFoundMinFiniteValueArgument = argumentValue;
                            }
                            if (!double.IsInfinity(functionValue) && functionValue > lastFoundMaxFiniteValue)
                            {
                                lastFoundMaxFiniteValue = functionValue;
                                lastFoundMaxFiniteValueArgument = argumentValue;
                            }
                        }
                    }
                }
            }
            else
            {
                double step = theInterval.Length / stepsCount;
                for (double argumentValue = theInterval.MinValue; argumentValue <= theInterval.MaxValue; argumentValue += step)
                {
                    double functionValue = GetValue(argumentValue);
                    if (!double.IsNaN(functionValue))
                    {
                        if (functionValue < lastFoundMinValue)
                        {
                            lastFoundMinValue = functionValue;
                            lastFoundMinValueArgument = argumentValue;
                        }
                        if (functionValue > lastFoundMaxValue)
                        {
                            lastFoundMaxValue = functionValue;
                            lastFoundMaxValueArgument = argumentValue;
                        }
                        if (!double.IsInfinity(functionValue) && functionValue < lastFoundMinFiniteValue)
                        {
                            lastFoundMinFiniteValue = functionValue;
                            lastFoundMinFiniteValueArgument = argumentValue;
                        }
                        if (!double.IsInfinity(functionValue) && functionValue > lastFoundMaxFiniteValue)
                        {
                            lastFoundMaxFiniteValue = functionValue;
                            lastFoundMaxFiniteValueArgument = argumentValue;
                        }
                    }
                }
            }
            lastUsedArgumentInterval = theInterval;
        }
       
        public ContinousPlotDataSet(FunctionDelegate function)
        {
            this.function = function;
        }

        public override double GetValue(double argument)
        {
            double result = double.NaN;
            try
            {
                result = function(argument);
            }
            catch (Exception) { }
            return result;
        }

    }
}
